﻿using System;
using System.Collections.Generic;
namespace Core
{

    public class Entity:IDisposable
    {
        private Entity parent;
        private Dictionary<long, Entity> children;
        private Dictionary<Type, Component> components;

        public long Id { get; internal set; }
        public Dictionary<long, Entity> Children => children ??= new Dictionary<long, Entity>();
        private Dictionary<Type, Component> Components => components ??= new Dictionary<Type, Component>();

        public Entity Parent
        {
            get
            {
                return parent;
            }
            set
            {
                if(parent!=null)
                {
                    parent.Remove(this);
                }
                if(value!=null)
                {
                    value.Add(this);
                }
            }
        }

        public void Add(Entity entity)
        {
            if (entity == null)
                return;

            entity.parent = this;
            this.Children.Add(entity.Id, entity);
        }
        public void Remove(Entity entity)
        {
            if (this.children == null|| entity == null)
                return;

            this.children.Remove(entity.Id);
            entity.parent = null;
        }

        public K GetChild<K>(long id) where K : Entity
        {
            if (this.children == null)
                return null;
            this.children.TryGetValue(id, out Entity entity);
            return entity as K;
        }

        public K GetComponent<K>() where K : Component
        {
            Type type = typeof(K);
            if (this.components == null)
                return null;
            this.components.TryGetValue(type, out Component component);
            return component as K;
        }

        public void RemoveComponent(Type type)
        {
            if (this.components == null)
                return;
            this.components.TryGetValue(type, out Component component);
            this.components.Remove(type);
            component?.Dispose();
        }

        public void RemoveComponent<K>()where K:Component
        {
            RemoveComponent(typeof(K));
        }

        public Component AddComponent(Type type)
        {
            if (this.components != null && this.components.ContainsKey(type))
                throw new Exception($"entity already has component: {type.FullName}");
            Component component = Activator.CreateInstance(type) as Component;
            component.Parent = this;
            Components.Add(type, component);

            return component;
        }

        public K AddComponent<K>()where K : Component
        {
            return AddComponent(typeof(K)) as K;
        }

        public K AddComponent<K, V1>(V1 v1) where K : Component
        {
            var type = typeof(K);
            if (this.components != null && this.components.ContainsKey(type))
                throw new Exception($"entity already has component: {type.FullName}");
            Component component = Activator.CreateInstance(type, new object[] { v1 }) as Component;
            component.Parent = this;
            Components.Add(type, component);

            return component as K;
        }
        public K AddComponent<K, V1, V2>(V1 v1, V2 v2) where K : Component
        {
            var type = typeof(K);
            if (this.components != null && this.components.ContainsKey(type))
                throw new Exception($"entity already has component: {type.FullName}");
            Component component = Activator.CreateInstance(type, new object[] { v1, v2 }) as Component;
            component.Parent = this;
            Components.Add(type, component);

            return component as K;
        }

        public K AddComponent<K, V1, V2, V3>(V1 v1, V2 v2, V3 v3) where K : Component
        {
            var type = typeof(K);
            if (this.components != null && this.components.ContainsKey(type))
                throw new Exception($"entity already has component: {type.FullName}");
            Component component = Activator.CreateInstance(type, new object[] { v1, v2,v3 }) as Component;
            component.Parent = this;
            Components.Add(type, component);

            return component as K;
        }

        public virtual void Dispose()
        {
            foreach (var child in children.Values)
            {
                child.Dispose();
            }
            foreach (var component in components.Values)
            {
                component.Dispose();
            }
            Parent = null;
            children.Clear();
            components.Clear();
        }

        public static K Create<K>() where K : Entity
        {
            var entity = (K)Activator.CreateInstance(typeof(K));
            entity.Id = IdGenerater.GenerateId();
            return entity;
        }
        public static K Create<K,V>(V v) where K : Entity
        {
            var entity = (K)Activator.CreateInstance(typeof(K),new object[] { v});
            entity.Id = IdGenerater.GenerateId();
            return entity;
        }
        public static K Create<K, V, V2>(V v, V2 v2) where K : Entity
        {
            var entity = (K)Activator.CreateInstance(typeof(K), new object[] { v, v2 });
            entity.Id = IdGenerater.GenerateId();
            return entity;
        }
        public static K Create<K, V, V2,V3>(V v, V2 v2,V3 v3) where K : Entity
        {
            var entity = (K)Activator.CreateInstance(typeof(K), new object[] { v, v2,v3 });
            entity.Id = IdGenerater.GenerateId();
            return entity;
        }

    }

}

